package com.acme.bankapp.service.bank;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Calendar;

import com.acme.bankapp.domain.bank.Account;
import com.acme.bankapp.domain.bank.Bank;
import com.acme.bankapp.domain.bank.ClientRegistrationListener;
import com.acme.bankapp.domain.bank.SavingsAccount;
import com.acme.bankapp.domain.bank.CheckingAccount;
import com.acme.bankapp.domain.bank.Client;
import com.acme.bankapp.domain.bank.Gender;
import com.acme.bankapp.domain.bank.BankException;
import com.acme.bankapp.service.bank.BankDataLoaderService;
import com.acme.bankapp.service.bank.BankDataLoaderService.FeedLoadException;

public class BankService {
	
	static String serializeFileName;
	
	@SuppressWarnings("serial")
	public static class PrintClientListener implements ClientRegistrationListener, Serializable {
		public void onClientAdded(Client c) {
			System.out.println("New client "+c.getClientSalutation()+": "+c.getAccount().maximumAmountToWithdraw());
		}		
	}
	
	@SuppressWarnings("serial")
	public static class EmailNotificationListener implements ClientRegistrationListener, Serializable {
		public void onClientAdded(Client c) {
			System.out.println("Notification email for client "+c.getClientSalutation()+" to be sent");
		}		
	}
	
    @SuppressWarnings("serial")
	public static class DebugListener implements ClientRegistrationListener, Serializable {
    	public void onClientAdded(Client c) {
    		Calendar calendar = Calendar.getInstance();
        	SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
    		System.out.println("Client "+c.getClientSalutation()+" "+format.format(calendar.getTime()));
    	}
    }	
    
    public static Bank createBank(String[] args) throws IOException, FeedLoadException {
    	serializeFileName = "bank.ser";
    	
    	if (args.length == 2) {
    		if (args[0].equals("-loadbank")) {
    			serializeFileName = args[1];
    			return readBank(serializeFileName);
    		} else if (args[0].equals("-feedbank")) {
    			return feedBank(args[1]);
    		}
    	}
    	
    	Bank newBank = createBankWithListeners();
    	generateClients(newBank, 7);
    	return newBank;
    }
    
    public static Bank createBankWithListeners() {
		ClientRegistrationListener [] listeners = new ClientRegistrationListener[3];
		listeners[0] = new PrintClientListener();
		listeners[1] = new EmailNotificationListener();
		listeners[2] = new DebugListener();
    	return new Bank(listeners);
    }	
    
	static void addClient(Bank bank, Client client)
	{
		bank.addClient(client);
	}
	
	public static void generateClients(Bank bank, int number)
	{
		for (int i = 0; i < number; i++)
		{
			addClient(bank, generateClient());
		}		
	}
	
	public static Client generateClient()
	{
		String[][] names = { {"Andrey","Ivan","Gleb","Vasiliy","Oleg","Stanislav"},
				 {"Diana","Olga","Alexandra","Natalia","Maria"} };
		
		Account newAccount;
		try {
			newAccount = Math.random() < 0.5 ? new SavingsAccount(Math.random() * 100 - 10) : new CheckingAccount(Math.random() * 100 - 10, Math.random() * 100 - 10);
		}
		catch (IllegalArgumentException e) {
			newAccount = Math.random() < 0.5 ? new SavingsAccount(0) : new CheckingAccount(0, 0);
		}
		Gender newGender = Math.random() < 0.5 ? Gender.MALE : Gender.FEMALE; 
		String newName = names[newGender.ordinal()][((int)(Math.random()*100)) % names[newGender.ordinal()].length];
		
		return new Client(newName, newGender, newAccount);		
	}	
	
	static void modifyBank(Bank bank) throws BankException, IOException
	{
		for (int i = 0; i < bank.numClients(); i++) 
		{
			if (Math.random() < 0.5) 
			{
				double amount = Math.random() * 5;
				if (Math.random() < 0.5) 
				{
					bank.getClient(i).getAccount().withdraw(amount);
				} 
				else 
				{
					bank.getClient(i).getAccount().deposit(amount);
				}
			}
		}
		writeBank(bank, serializeFileName);
	}
	
	static void printMaximumAmountToWithdraw(Bank bank) 
	{
		System.out.println("List of clients:");
		for (int i = 0; i < bank.numClients(); i++) 
		{
			System.out.println(bank.getClient(i).getClientSalutation()+": "+bank.getClient(i).getAccount().maximumAmountToWithdraw());
		}
	}		
	
	static void printClients(Bank bank) {
		System.out.println("List of clients:");
		StringBuffer sbuffer = new StringBuffer();
		for (int i = 0; i < bank.numClients(); i++) {
			sbuffer.append(bank.getClient(i)+"\n");
		}
		System.out.println(sbuffer);
		System.out.println("");
	}
	
	static Bank feedBank(String fileName) throws BankDataLoaderService.FeedLoadException {
		ClientRegistrationListener [] listeners = new ClientRegistrationListener[3];
		listeners[0] = new PrintClientListener();
		listeners[1] = new EmailNotificationListener();
		listeners[2] = new DebugListener();
    	Bank bank = new Bank(listeners);
    	BankDataLoaderService.feedLoadBank(bank, fileName);
    	return bank;
	}
	
	static void writeBank(Bank bank, String fileName) throws IOException {
		FileOutputStream fos = null;
		BufferedOutputStream bos = null;
		ObjectOutputStream oos = null;
		try {
			fos = new FileOutputStream(fileName);
			try {
				bos = new BufferedOutputStream(fos);
				try {
					oos = new ObjectOutputStream(bos);
					oos.writeObject(bank);
				} finally {
					if (oos != null) oos.close();
				}
			} finally {
				if (bos != null) bos.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (fos != null) fos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
    
    static Bank readBank(String fileName) {
    	Bank newBank = null;
		FileInputStream fis = null;
		BufferedInputStream bis = null;
		ObjectInputStream ois = null;
		try {
			fis = new FileInputStream(fileName);
			try {
				bis = new BufferedInputStream(fis);
				try {
					ois = new ObjectInputStream(bis);
					try {
						newBank = (Bank)ois.readObject();
					} catch (Exception e) {
						e.printStackTrace();
					}
				} finally {
					if (ois != null) ois.close();
				}
			} finally {
				if (bis != null) bis.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (fis != null) fis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return newBank;
    }	
	
}
